package cn.t.service.user;


import cn.t.constants.CommonConstants;
import cn.t.core.exception.BizException;
import cn.t.core.mode.YesNoEnum;
import cn.t.core.service.ServiceImpl;
import cn.t.core.enums.base.BaseStatusEnum;
import cn.t.enums.user.ScenarioCodeEnum;
import cn.t.enums.user.UserError;
import cn.t.model.user.UserQueryDO;
import cn.t.repository.user.UserQueryRepository;
import cn.t.user.converter.ParticipantVOConverter;
import cn.t.user.converter.UserQueryVOConverter;
import cn.t.utils.JsonUtil;
import cn.t.vo.user.FirmVO;
import cn.t.vo.user.ParticipantVO;
import cn.t.vo.user.ParticipantWithUserVO;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author l
 */
@Slf4j
@Service
@AllArgsConstructor
public class UserQueryService extends ServiceImpl<UserQueryDO, UserQueryRepository> {

    public List<UserQueryDO> findByUserIds(List<Long> userIds) {
        UserQueryDO template = new UserQueryDO();
        template.setIds(userIds);
        template.setUserStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        template.setIsDeleted(YesNoEnum.FALSE);

        return repository.findByModel(template, 0, 10000, "id", "asc");
    }

    public void disableUserQuery(Long userId) {

        UserQueryDO template = new UserQueryDO();
        template.setUserStatus(String.valueOf(BaseStatusEnum.DISABLE.getCode()));
        template.setGmtModified(new Date());
        template.setId(userId);

        repository.update(template);

    }

    public void saveOrUpdate(UserQueryDO template) {
        UserQueryDO count = new UserQueryDO();
        count.setId(template.getId());

        List<UserQueryDO> tmp = repository.findByModel(count, 0, 100, null, null);

        if (repository.count(count) > 0) {
            template.setModifier(template.getModifier());
            template.setGmtModified(new Date());
            this.update(template);
        } else {
            template.setCreator(template.getCreator());
            template.setGmtCreated(new Date());
            this.add(template);
        }
    }

    public UserQueryDO findByUserId(Long userId) {
        UserQueryDO queryCondition = new UserQueryDO();
        queryCondition.setId(userId);
        queryCondition.setUserStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        queryCondition.setIsDeleted(YesNoEnum.FALSE);

        List<UserQueryDO> userQueryList = repository.findByModel(queryCondition, 0, 1, "gmt_created", "asc");
        if (CollectionUtils.isEmpty(userQueryList)) {
            return null;
        }

        return userQueryList.get(0);
    }

    public UserQueryDO findByUserId(Long firmId, Long userId, boolean force) {
        UserQueryDO queryCondition = new UserQueryDO();
        queryCondition.setId(userId);
        if (!force) {
            queryCondition.setUserStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        }
        queryCondition.setIsDeleted(YesNoEnum.FALSE);

        List<UserQueryDO> userQueryList = repository.findByModel(queryCondition, 0, 1, "gmt_created", "asc");
        if (CollectionUtils.isEmpty(userQueryList)) {
            return null;
        }

        UserQueryDO userQuery = userQueryList.get(0);
        if (!String.valueOf(BaseStatusEnum.AVAILABLE.getCode()).equals(userQuery.getUserStatus())) {
            log.warn("用户不可用：userId={} firmID={}", userId, firmId);
            throw new BizException(UserError.PASSPORT_IS_NOT_AVAILABLE, UserError.PASSPORT_IS_NOT_AVAILABLE.getName());
        }

        List<FirmVO> firms = userQueryVOConverter.convertFirms(userQuery.getParticipantJson());
        if (CollectionUtils.isEmpty(firms)) {
            if (force) {
                log.warn("用户不归属当前企业：userId={} firmID={}", userId, firmId);
                throw new BizException(UserError.PERMISSION_DENIED, UserError.PERMISSION_DENIED.getName());
            } else {
                return null;
            }
        }

        for (FirmVO firm : firms) {
            if (firmId.equals(firm.getCustomerId())) {
                return userQuery;
            }
        }

        if (force) {
            log.warn("用户不归属当前企业：userId={} firmID={}", userId, firmId);
            throw new BizException(UserError.PERMISSION_DENIED, UserError.PERMISSION_DENIED.getName());
        } else {
            return null;
        }
    }

    public UserQueryDO findByUserId(Long firmId, Long userId) {
        return findByUserId(firmId, userId, false);
    }

    public List<UserQueryDO> findByUserName(Long firmId, String userName) {

        UserQueryDO queryCondition = new UserQueryDO();
        queryCondition.setFirmId(firmId);
        queryCondition.setUserName(userName);
        queryCondition.setUserStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        queryCondition.setIsDeleted(YesNoEnum.FALSE);

        List<UserQueryDO> userQueryList = repository.findByModel(queryCondition, 0, 100, "gmt_created", "asc");
        if (CollectionUtils.isEmpty(userQueryList)) {
            return null;
        }
        List<UserQueryDO> result = new ArrayList<>();

        for (UserQueryDO userQueryDO : userQueryList) {
            if (userName.equals(userQueryDO.getUserName())) {
                result.add(userQueryDO);
            }
        }

        return result;
    }


    public List<UserQueryDO> findByCardCode(Long firmId, String cardCode) {

        UserQueryDO queryCondition = new UserQueryDO();
        queryCondition.setFirmId(firmId);
        queryCondition.setCardCode(cardCode);
        queryCondition.setIsDeleted(YesNoEnum.FALSE);

        List<UserQueryDO> userQueryList = repository.findByModel(queryCondition, 0, 100, "gmt_created", "asc");
        if (CollectionUtils.isEmpty(userQueryList)) {
            return null;
        }
        List<UserQueryDO> result = new ArrayList<>();

        for (UserQueryDO userQueryDO : userQueryList) {
            if (cardCode.equals(userQueryDO.getCardCode())) {
                result.add(userQueryDO);
            }
        }

        return result;
    }

    public List<UserQueryDO> findByEmployeeCode(Long firmId, String employeeCode) {

        UserQueryDO queryCondition = new UserQueryDO();
        queryCondition.setFirmId(firmId);
        queryCondition.setEmployeeCode(employeeCode);
        queryCondition.setIsDeleted(YesNoEnum.FALSE);

        List<UserQueryDO> userQueryList = repository.findByModel(queryCondition, 0, 100, "gmt_created", "asc");
        if (CollectionUtils.isEmpty(userQueryList)) {
            return null;
        }

        List<UserQueryDO> result = new ArrayList<>();

        for (UserQueryDO userQueryDO : userQueryList) {
            if (employeeCode.equals(userQueryDO.getEmployeeCode())) {
                result.add(userQueryDO);
            }
        }

        return result;
    }

    public ParticipantWithUserVO findByFirmCardCode(UserQueryDO queryCondition) {

        //防止查询条件被污染
        UserQueryDO template = new UserQueryDO();
        BeanUtils.copyProperties(queryCondition, template);

        List<UserQueryDO> userQueryList = repository.findByModel(template, 0, 100, null, null);

        if (userQueryList == null || userQueryList.isEmpty()) {
            return null;
        }

        if (userQueryList.size() > 1) {
            log.warn("Too many users. conditions={} result={}", queryCondition, userQueryList);
        }

        for (UserQueryDO userQuery : userQueryList) {
            boolean isTarget = false;

            if (StringUtils.isNotBlank(queryCondition.getCardCode()) && queryCondition.getCardCode().equals(userQuery.getCardCode())) {
                isTarget = true;
            }

            if (!isTarget) {
                break;
            }

            List<FirmVO> tmp = userQueryVOConverter.convertFirms(userQuery.getParticipantJson());
            if (CollectionUtils.isEmpty(tmp)) {
                return null;
            }

            for (FirmVO firm : tmp) {
                if (queryCondition.getFirmId() == null || !queryCondition.getFirmId().equals(firm.getCustomerId())) {
                    continue;
                }

                if (CollectionUtils.isEmpty(firm.getParticipants())) {
                    continue;
                }

                for (ParticipantVO participant : firm.getParticipants()) {

                    ParticipantWithUserVO ret = participantVOConverter.toParticipantWithUserVO(participant);

                    ret.setFirmId(firm.getCustomerId());
                    ret.setFirmName(firm.getFirmName());
                    ret.setFirmCode(firm.getFirmCode());

                    ret.setUserId(userQuery.getId());
                    ret.setUserName(userQuery.getUserName());
                    ret.setIndividualId(Long.valueOf(userQuery.getIndividualId()));
                    ret.setIndividualName(userQuery.getIndividualName());
                    ret.setEmployeeCode(userQuery.getEmployeeCode());

                    return ret;
                }
            }
        }
        return null;
    }

    public ParticipantWithUserVO findMaterIssuerByFirmCard(Long firmId, String cardCode) {

        UserQueryDO template = new UserQueryDO();
        template.setFirmId(firmId);
        template.setCardCode(cardCode);
        template.setScenarioCode(String.valueOf(ScenarioCodeEnum.MATERIAL_ISSUE.getCode()));

        return findByFirmCardCode(template);

    }

    public List<UserQueryDO> findByFirmIdIgnoreDisable(Long firmId) {
        UserQueryDO template = new UserQueryDO();
        template.setFirmId(firmId);
        return findByModel(template, 0, 10000, null, null);
    }

    public List<UserQueryDO> findByFirmId(Long firmId) {
        UserQueryDO template = new UserQueryDO();
        template.setFirmId(firmId);
        template.setIsDeleted(YesNoEnum.FALSE);
        template.setUserStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));

        return findByModel(template, 0, Integer.MAX_VALUE, null, null);
    }

    public List<UserQueryDO> findUserByFirmId(UserQueryDO userQueryDO) {
        List<UserQueryDO> byModel = repository.findByModel(userQueryDO, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
        return byModel.stream().filter(userQueryVo -> ObjectUtils.isNotEmpty(userQueryVo.getParticipantJson())).map(userQueryVo -> {
            JavaType type = objectMapper.getTypeFactory().constructCollectionType(List.class, FirmVO.class);
            try {
                userQueryVo.setFirms(objectMapper.readValue(userQueryVo.getParticipantJson(), type));
            } catch (JsonProcessingException e) {
                log.warn("Read participant from JSON fault. {}", userQueryVo.getParticipantJson());
                return null;
            }
            return userQueryVo;
        }).filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());

    }

    public List<UserQueryDO> findByCostCenterId(Long costCenterId) {
        UserQueryDO template = new UserQueryDO();
        template.setCostCenterId(costCenterId);
        return findByModel(template, 0, 10000, null, null);
    }

    public UserQueryDO findOne(String employeeCode, String cardCode, Long firmId) {

        if (StringUtils.isBlank(employeeCode) && StringUtils.isBlank(cardCode)) {
            return null;
        }

        UserQueryDO template = new UserQueryDO();
        template.setEmployeeCode(employeeCode);
        template.setCardCode(cardCode);
        List<UserQueryDO> userQueries = findByModel(template, 0, 10000, null, null);

        if (CollectionUtils.isEmpty(userQueries)) {
            return null;
        }

        UserQueryDO userQuery = userQueries.get(0);
        // 获取成本中心等公司相关信息
        if (firmId != null) {
            List<FirmVO> firms = JsonUtil.parseList(userQuery.getParticipantJson(), FirmVO.class);

            firms.stream().filter(f -> firmId.equals(f.getCustomerId())).findFirst().ifPresent(f -> {

                if (!CollectionUtils.isEmpty(f.getParticipants())) {
                    f.getParticipants().stream().filter(ff -> ff.getCostCenterId() != null).findFirst().ifPresent(ff -> {
                        userQuery.setCostCenterId(ff.getCostCenterId());
                        userQuery.setCostCenterCode(ff.getCostCenterCode());
                        userQuery.setCostCenterName(ff.getCostCenterName());
                    });
                }

                userQuery.setFirmName(f.getFirmName());
                userQuery.setFirmCode(f.getFirmCode());
            });

        }

        return userQuery;
    }

    @Resource
    private ObjectMapper objectMapper;


    @Autowired
    private ParticipantVOConverter participantVOConverter;

    @Autowired
    private UserQueryVOConverter userQueryVOConverter;


}
